#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace IPSim.Util
{
    /// <summary>
    /// LogLevel struct
    /// </summary>
    public delegate void LogDelegate(string msg, LogLevel lvl);
    
    public struct LogLevel
    {
        private String m_levelString;
        private byte m_levelValue;
        public override String ToString()
        {
            return m_levelString + ":";
        }
        LogLevel(String lvlString, byte lvlValue)
        {
            m_levelString = lvlString;
            m_levelValue = lvlValue;
        }

        public byte LevelValue
        {
            get
            {
                return m_levelValue;
            }
        }

        public static LogLevel VERBOSE = new LogLevel("VERBOSE", 1);
        public static LogLevel DEBUG = new LogLevel("DEBUG", 2);
        public static LogLevel INFO = new LogLevel("INFO", 3);
        public static LogLevel WARNING = new LogLevel("WARNING", 4);
        public static LogLevel ERROR = new LogLevel("ERROR", 5);
        public static LogLevel FATAL = new LogLevel("FALAT", 6);

    }

    /// <summary>
    /// Tracer class.
    /// </summary>
    public static class Logger
    {
        /// <summary>
        /// Default:
        /// Level: VERBOSE
        /// File: IPSimLog.txt
        /// Mode: Rewrite existing log file
        /// </summary>
        private static LogLevel m_currentLevel = LogLevel.VERBOSE;
        private const String m_logFileName = "IPSimLog.txt";
        private const FileMode m_logFileMode = FileMode.Create;
        private static bool m_logToFile = false;
        private static bool m_logToScreen = false;

        private static FileStream m_fileStream;
        private static StreamWriter m_streamWriter;

        private static LogDelegate m_logDelegate;

        public static void log(string message)
        {
            logNotifier(message, LogLevel.INFO);            
        }

        public static void log(Object obj)
        {
            logNotifier(obj.ToString(), LogLevel.INFO);
        }

        public static void log(Object obj, LogLevel level)
        {
            logNotifier(obj.ToString(), level);
        }

        public static void log(string msg, LogLevel level)
        {
            logNotifier(msg, level);
        }

        static Logger()
        {
            if (m_logToFile)
            {
                m_logDelegate += new LogDelegate(logToFile);
                Logger.log("Opening File stream for File input", LogLevel.INFO);
                m_fileStream = new FileStream(m_logFileName, m_logFileMode, FileAccess.Write);
                m_streamWriter = new StreamWriter(m_fileStream);
                if (m_streamWriter != null)
                    Logger.log("File stream opened successfuly", LogLevel.INFO);
                else
                    Logger.log("File stream opening failed", LogLevel.ERROR);
            }
        }

        public static void Subscribe(LogDelegate logDelegate)
        {
            m_logDelegate += logDelegate;
        }

        public static void Unubscribe(LogDelegate logDelegate)
        {
            m_logDelegate -= logDelegate;
        }     

        public static void logNotifier(string message, LogLevel level)
        {
            if(m_logDelegate!=null)
                m_logDelegate(message, level);
        }
        /// <summary>
        /// using StackFrame for stack unwinding. Appenging caller method to the end of message.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="type"></param>
        public static void logToFile(string message, LogLevel level)
        {
            // Gets stack frame from upper level to read caller method name
            StackFrame sf = new StackFrame(1, true);
            System.Reflection.MethodBase mb = sf.GetMethod();
            string methodName = mb != null ? mb.Name : "";
            if (level.LevelValue >= m_currentLevel.LevelValue)
            {
                if (m_logToScreen)
                {
                    Console.WriteLine(System.DateTime.Now.ToString("HH:mm:ss    ") + level + message + " [Stack: " + sf.GetMethod().DeclaringType.Name + "]");
                }
                if (m_streamWriter != null)
                {
                    m_streamWriter.WriteLine(System.DateTime.Now.ToString("HH:mm:ss    ") + level + "\t" + message + " [Stack: " + sf.GetMethod().DeclaringType.Name + "]");
                    m_streamWriter.Flush();
                }
            }
        }
    }
}
